#!/usr/bin/env python

# repo-rehab: like repo, without the enterprise cruft

# TODO
#
# * Handle multiple named remotes
# * Clone only missing projects
# * Add remotes to existing projects
# * Add support for an untracked local_repos.json

import json
import os
import sys
import argparse
import posixpath
import subprocess

def warning(msg):
    sys.stderr.write(msg + "\n")

def error(msg):
    sys.stderr.write(msg + "\n")
    sys.exit(1)

def launch(argv):
    return subprocess.check_output(argv).decode("ascii").strip().split("\n")

cwd = None
base_remote = None

def get_remote(path):
    # It's an absolute URL
    if ":" in path:
        return path

    url = launch(["git", "config", "--get", "remote.{}.url".format(base_remote)])
    assert(len(url) == 1)
    url = url[0]

    prefix, original_path = url.split(":", 1)
    new_path = posixpath.normpath(posixpath.join(original_path, "..", path))
    return prefix + ":" + new_path

def main():
    parser = argparse.ArgumentParser(description="repo-rehab")
    parser.add_argument("command",
                        metavar="COMMAND",
                        help="Command to run: clone")
    parser.add_argument("--base-remote",
                        metavar="REMOTE",
                        nargs="?",
                        default="",
                        help="Remote to use as a base for cloning new repos")
    parser.add_argument("--branch",
                        metavar="BRANCH",
                        help="Check out BRANCH after clone.")
    args = parser.parse_args()

    global cwd
    cwd = os.getcwd()
    if not os.path.exists(os.path.join(cwd, ".git")):
        error("Not in a git repo")

    # Collect default remote
    global base_remote
    base_remote = args.base_remote
    if base_remote == "":
        remotes = launch(["git", "remote"])
        if len(remotes) != 1:
            error("No base remote specified and more than one available")
        else:
            base_remote = remotes[0]

    config = json.load(open("repos.json", "r"))

    command = args.command
    if command == "clone":
        default_remote = get_remote(config["default_remote"])
        for project in config["projects"]:
            checkout_path = os.path.abspath(project["path"])

            # Check if the target directory is empty
            if  os.path.exists(checkout_path) and \
                len(os.listdir(checkout_path)) > 0:
                warning("\"{}\" is not empty, skipping"
                        .format(project["path"]))
            else:
                # Compute the remote to clone
                remote = get_remote(project["remote"]) if "remote" in project \
                         else default_remote

                # Clone the repository
                url = posixpath.join(remote, project["name"])
                launch(["git", "clone", url, checkout_path])

                # Check out the request branch
                if len(args.branch) > 0:

                    # Look for one of the specified branches, in the specified
                    # order
                    for branch in args.branch.split(","):
                        remote_branches = launch(["git", "-C", checkout_path, "for-each-ref", "refs/remotes/" + base_remote, "--format", "%(refname)"])
                        matching = list(filter(lambda name: name.endswith("origin/" + branch), remote_branches))
                        if len(matching) == 0:
                            warning("Can't find {} branch for \"{}\"".format(branch, checkout_path))
                        elif len(matching) == 1:
                            # The branch has been found, check it out and stop
                            launch(["git", "-C", checkout_path, "checkout", branch])
                            break
                        else:
                            error("More than one branch matching: " + str(matching))

if __name__ == "__main__":
    main()
